package cn.xshi.sentinel.init;

import cn.xshi.sentinel.Constants;
import com.alibaba.csp.sentinel.cluster.ClusterStateManager;
import com.alibaba.csp.sentinel.cluster.flow.rule.ClusterFlowRuleManager;
import com.alibaba.csp.sentinel.cluster.flow.rule.ClusterParamFlowRuleManager;
import com.alibaba.csp.sentinel.cluster.server.config.ClusterServerConfigManager;
import com.alibaba.csp.sentinel.cluster.server.config.ServerFlowConfig;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.property.SentinelProperty;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.util.function.Function;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.nacos.api.PropertyKeyConst;
import cn.xshi.common.util.SpringUtils;
import cn.xshi.sentinel.util.FlowRuleUtil;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.Properties;
import java.util.Set;

@Slf4j
public class FlowRuleInitFunc implements InitFunc{
    /**
     * nacos properties（配置中心自身属性 如可以指定访问Nacos下的Name pace 否则只能访问Nacos public空间下的文件配置）
     */
    private Properties properties;

    FlowRuleUtil flowRuleUtil;

    @Override
    public void init() throws Exception{
        flowRuleUtil = SpringUtils.getBean(FlowRuleUtil.class);
        initProperties();
        initPropertySupplier();//初始化监听特定namespace（集群的namespace或客户端项目名）下的集群限流规则
        registerTokenServerClientNamespaces();//注册tokenServer管辖的作用域(即管理哪些应用)
//        registerFlowRule();
//        registerParamRule();
        registerGlobalServerFlow();
    }

    /**
     * 初始化 Nacos自身属性配置 目的为接下面方法远程访问该属性下文件
     */
    private void initProperties() {
        this.properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, flowRuleUtil.getNacosAddress());
        properties.put(PropertyKeyConst.NAMESPACE, flowRuleUtil.getNacosNamespace());
        properties.put(PropertyKeyConst.USERNAME,flowRuleUtil.getUsername());
        properties.put(PropertyKeyConst.PASSWORD,flowRuleUtil.getPassword());
    }

    private void initPropertySupplier(){
        log.info("开始注册热点规则，流控规则...");
        // Register cluster flow rule property supplier which creates data source by namespace.
        // Flow rule dataId format: ${namespace}-flow-rules
        ClusterFlowRuleManager.setPropertySupplier(namespace -> {
            ReadableDataSource<String, List<FlowRule>> ds = new NacosDataSource<>(properties, flowRuleUtil.getGroupId(),
                    namespace + Constants.FLOW_RULES_POSTFIX,
                    source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
            return ds.getProperty();
        });

        // Register cluster parameter flow rule property supplier which creates data source by namespace.
        ClusterParamFlowRuleManager.setPropertySupplier(namespace -> {
            ReadableDataSource<String, List<ParamFlowRule>> ds = new NacosDataSource<>(properties, flowRuleUtil.getGroupId(),
                    namespace + Constants.PARAM_RULES_POSTFIX, source -> JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {}));
            return ds.getProperty();
        });
        log.info("注册热点规则，流控规则完毕...");

    }


    /**
     * 通过Nacos中拉取管理客户端的token_server_client_namespace_set.json
     */
    private void registerTokenServerClientNamespaces(){
        log.info("注册Namespace...");
        ReadableDataSource<String, Set<String>> namespaceDs = new NacosDataSource<>(properties, flowRuleUtil.getGroupId(),
                flowRuleUtil.getTokenServerClientNamespaceSet(), new Converter<String, Set<String>>() {
            @Override
            public Set<String> convert(String source) {
                return JSON.parseObject(source, new TypeReference<Set<String>>() {
                });
            }
        });
        ClusterServerConfigManager.registerNamespaceSetProperty(namespaceDs.getProperty());
    }



    /**
     * 管理普通限流
     * 通过Nacos中拉取所有规则（规则DATA-ID为：客户端1-flow-rules）
     *
     * 示例：namespace+ Constants.FLOW_RULES_POSTFIX
     *
     * FLOW_RULES_POSTFIX：-flow-rules
     *
     * namespace：["客户端1","客户端2","客户端3","客户端N"]
     *
     * 注册进来后则会出现：客户端1-flow-rules，客户端2-flow-rules，客户端3-flow-rules，客户端N-flow-rules
     *
     * 客户端N-flow-rules具体JSON如下：
     *
     [{
     "clusterConfig": {
     "fallbackToLocalWhenFail": true,
     "thresholdType": 0,
     "windowIntervalMs": 1000,
     "flowId": 11112
     },
     "clusterMode": true,
     "controlBehavior": 0,
     "count": 2,
     "grade": 1,
     "limitApp": "default",
     "resource": "/lcTaskHis/list"
     }]
     *
     */
    @Deprecated
    private void registerFlowRule(){
        log.info("注册流控规则...");
        //与客户端区别是Token server服务端 需要的是管理客户端 所以采用ClusterFlowRuleManager设置将规则全部列入进来
        ClusterFlowRuleManager.setPropertySupplier(new Function<String, SentinelProperty<List<FlowRule>>>() {
            @Override
            public SentinelProperty<List<FlowRule>> apply(String namespace) {
                ReadableDataSource<String, List<FlowRule>> flowRuleDs =
                        new NacosDataSource<List<FlowRule>>(properties, flowRuleUtil.getGroupId(), namespace + Constants.FLOW_RULES_POSTFIX,//（此时的namespace=loadTokenServerClientNamespaces中将token_server_client_namespace_set加载到了namespace中了）等同于在main方法中启动时候增加如下示例：ClusterServerConfigManager.loadServerNamespaceSet( Collections.singleton("token-client"));，但是这种写死了不灵活
                                new Converter<String, List<FlowRule>>() {
                                    @Override
                                    public List<FlowRule> convert(String source) {
                                        return JSON.parseObject(source, new TypeReference<List<FlowRule>>() {
                                        });
                                    }
                                });
                return flowRuleDs.getProperty();
            }
        });
    }

    /**
     * 管理热点参数限流
     *
     * 通过Nacos中拉取所有规则（规则DATA-ID为：客户端1-param-rules）
     *
     * 示例：namespace+ Constants.FLOW_RULES_POSTFIX
     *
     * FLOW_RULES_POSTFIX：-param-rules
     *
     * namespace：["客户端1","客户端2","客户端3","客户端N"]
     *
     * 注册进来后则会出现：客户端1-param-rules，客户端2-param-rules，客户端3-param-rules，客户端N-param-rules
     *
     * 客户端N-param-rules具体JSON如下：
     *
     * 缺省
     *
     */
    @Deprecated
    private void registerParamRule(){
        log.info("注册流控热点参数规则...");
        ReadableDataSource<String, List<ParamFlowRule>> paramRuleSource = new NacosDataSource<>(properties, flowRuleUtil.getGroupId(), Constants.PARAM_RULES_POSTFIX, new Converter<String, List<ParamFlowRule>>() {
            @Override
            public List<ParamFlowRule> convert(String source) {
                return JSON.parseObject(source, new TypeReference<List<ParamFlowRule>>() {
                });
            }
        });
        ParamFlowRuleManager.register2Property(paramRuleSource.getProperty());
    }


    /**
     * 全局设置最大QPS
     */
    private void registerGlobalServerFlow(){
        ReadableDataSource<String, ServerFlowConfig> serverFlowConfig = new NacosDataSource<>(properties, flowRuleUtil.getGroupId(),
                flowRuleUtil.getTokenServerFlow(), new Converter<String, ServerFlowConfig>() {
            @Override
            public ServerFlowConfig convert(String source) {
                return JSON.parseObject(source, new TypeReference<ServerFlowConfig>() {
                });
            }
        });
        ClusterServerConfigManager.registerGlobalServerFlowProperty(serverFlowConfig.getProperty());
    }

    /**
     * 设置应用状态,表示服务端
     */
    @Deprecated
    private void initStateProperty() {
        ClusterStateManager.applyState ( ClusterStateManager.CLUSTER_SERVER );
    }
}